

#include "port/GFX_SSD1331.h"

namespace GFX {

/*!
 * @brief Select one of these defines to set the pixel color order
 */
#define SSD1331_COLORORDER_RGB
// #define SSD1331_COLORORDER_BGR

#if defined SSD1331_COLORORDER_RGB && defined SSD1331_COLORORDER_BGR
#error "RGB and BGR can not both be defined for SSD1331_COLORODER."
#endif

// Timing Delays
#define SSD1331_DELAYS_HWFILL (3) //!< Fill delay
#define SSD1331_DELAYS_HWLINE (1) //!< Line delay

// SSD1331 Commands
#define SSD1331_CMD_DRAWLINE       0x21 //!< Draw line
#define SSD1331_CMD_DRAWRECT       0x22 //!< Draw rectangle
#define SSD1331_CMD_FILL           0x26 //!< Fill enable/disable
#define SSD1331_CMD_SETCOLUMN      0x15 //!< Set column address
#define SSD1331_CMD_SETROW         0x75 //!< Set row adress
#define SSD1331_CMD_CONTRASTA      0x81 //!< Set contrast for color A
#define SSD1331_CMD_CONTRASTB      0x82 //!< Set contrast for color B
#define SSD1331_CMD_CONTRASTC      0x83 //!< Set contrast for color C
#define SSD1331_CMD_MASTERCURRENT  0x87 //!< Master current control
#define SSD1331_CMD_SETREMAP       0xA0 //!< Set re-map & data format
#define SSD1331_CMD_STARTLINE      0xA1 //!< Set display start line
#define SSD1331_CMD_DISPLAYOFFSET  0xA2 //!< Set display offset
#define SSD1331_CMD_NORMALDISPLAY  0xA4 //!< Set display to normal mode
#define SSD1331_CMD_DISPLAYALLON   0xA5 //!< Set entire display ON
#define SSD1331_CMD_DISPLAYALLOFF  0xA6 //!< Set entire display OFF
#define SSD1331_CMD_INVERTDISPLAY  0xA7 //!< Invert display
#define SSD1331_CMD_SETMULTIPLEX   0xA8 //!< Set multiplex ratio
#define SSD1331_CMD_SETMASTER      0xAD //!< Set master configuration
#define SSD1331_CMD_DISPLAYOFF     0xAE //!< Display OFF (sleep mode)
#define SSD1331_CMD_DISPLAYON      0xAF //!< Normal Brightness Display ON
#define SSD1331_CMD_POWERMODE      0xB0 //!< Power save mode
#define SSD1331_CMD_PRECHARGE      0xB1 //!< Phase 1 and 2 period adjustment
#define SSD1331_CMD_CLOCKDIV       0xB3 //!< Set display clock divide ratio/oscillator frequency
#define SSD1331_CMD_PRECHARGEA     0x8A //!< Set second pre-charge speed for color A
#define SSD1331_CMD_PRECHARGEB     0x8B //!< Set second pre-charge speed for color B
#define SSD1331_CMD_PRECHARGEC     0x8C //!< Set second pre-charge speed for color C
#define SSD1331_CMD_PRECHARGELEVEL 0xBB //!< Set pre-charge voltage
#define SSD1331_CMD_VCOMH          0xBE //!< Set Vcomh voltge

/**************************************************************************/
/*!
    @brief   Initialize SSD1331 chip
    Connects to the SSD1331 over SPI and sends initialization procedure commands
    @param    freq  Desired SPI clock frequency
*/
/**************************************************************************/
static const uint8_t init_cfg[] = {
    // Initialization Sequence
    SSD1331_CMD_DISPLAYOFF, // 0xAE
    SSD1331_CMD_SETREMAP,   // 0xA0
#if defined SSD1331_COLORORDER_RGB
    0x72, // RGB Color
#else
    0x76, // BGR Color
#endif
    SSD1331_CMD_STARTLINE, // 0xA1
    0x0,
    SSD1331_CMD_DISPLAYOFFSET, // 0xA2
    0x0,
    SSD1331_CMD_NORMALDISPLAY, // 0xA4
    SSD1331_CMD_SETMULTIPLEX,  // 0xA8
    0x3F,                      // 0x3F 1/64 duty
    SSD1331_CMD_SETMASTER,     // 0xAD
    0x8E,
    SSD1331_CMD_POWERMODE, // 0xB0
    0x0B,
    SSD1331_CMD_PRECHARGE, // 0xB1
    0x31,
    SSD1331_CMD_CLOCKDIV,   // 0xB3
    0xF0,                   // 7:4 = Oscillator Frequency, 3:0 = CLK Div Ratio
                            // (A[3:0]+1 = 1..16)
    SSD1331_CMD_PRECHARGEA, // 0x8A
    0x64,
    SSD1331_CMD_PRECHARGEB, // 0x8B
    0x78,
    SSD1331_CMD_PRECHARGEC, // 0x8C
    0x64,
    SSD1331_CMD_PRECHARGELEVEL, // 0xBB
    0x3A,
    SSD1331_CMD_VCOMH, // 0xBE
    0x3E,
    SSD1331_CMD_MASTERCURRENT, // 0x87
    0x06,
    SSD1331_CMD_CONTRASTA, // 0x81
    0x91,
    SSD1331_CMD_CONTRASTB, // 0x82
    0x50,
    SSD1331_CMD_CONTRASTC, // 0x83
    0x7D,
    SSD1331_CMD_DISPLAYON, //--turn on oled panel

    // 0x00, 0x00, 0x00, //
};

GFX_SSD1331::GFX_SSD1331(GFX_PortBase &lcd, uint16_t w, uint16_t h) : GFX_disRGB(lcd, w, h) {}

GFX_SSD1331::~GFX_SSD1331() {}

void GFX_SSD1331::tftInit() { sendCommandBytes(init_cfg, sizeof(init_cfg)); }

void GFX_SSD1331::displayOn() { sendCommand(SSD1331_CMD_DISPLAYON); }

void GFX_SSD1331::displayOff() { sendCommand(SSD1331_CMD_DISPLAYOFF); }

void GFX_SSD1331::setRotation(uint8_t r) {
    GFX_disRGB::setRotation(r);
    switch (_rotation) {
        case 1:
            r = 0b01110001;
            break;
        case 2:
            r = 0b01100000;
            break;
        case 3:
            r = 0b01100011;
            break;
        default: // case 0:
            r = 0b01110010;
            break;
    }
    startWrite();
    writeCommand(SSD1331_CMD_SETREMAP);
    writeCommand(r);
    endWrite();
}

void GFX_SSD1331::writeAddrWindow(int16_t x, int16_t y, uint16_t w, uint16_t h) {
    if ((x != _currentX) || (w != _currentW)) {
        uint8_t cmd     = (_rotation & 0x01) ? SSD1331_CMD_SETROW : SSD1331_CMD_SETCOLUMN;
        uint8_t x_start = x + _xstart, x_end = x + w - 1 + _xstart;

        writeCommand(cmd);     // Column addr set
        writeCommand(x_start); // XSTART
        writeCommand(x_end);   // XEND
        _currentX = x;
        _currentW = w;
    }
    if ((y != _currentY) || (h != _currentH)) {
        uint8_t cmd     = (_rotation & 0x01) ? SSD1331_CMD_SETCOLUMN : SSD1331_CMD_SETROW;
        uint8_t y_start = y + _ystart, y_end = y + h - 1 + _ystart;

        writeCommand(cmd);     // Row addr set
        writeCommand(y_start); // YSTART
        writeCommand(y_end);   // YEND

        _currentY = y;
        _currentH = h;
    }
}

} // namespace GFX
